Skip to main content

Agency

Alternative name: Board, State Board

As we implied in our last section, the way Users obtain Earnings is according to collections of rules. We didn't invent them - they are modeled after real-life constraints. In the real life of (virtual or physical) pen, paper, stamps and signatures, a state-level Agency/Board defines rules according to which the interested parties - various type of professionals - bring proof of continuous education and get to keep their license(s).

This means that an Agency is a conglomerate of rules that have to be kept in a state of internal cohesion. From a modeling perspective, the real-life existence of State-level Agencies/Boards is an immense "free lunch" for us, architecturally; it is a lynchpin for our system. It's like when trying to fit a number of objects of various size in a box - the largest one(s) will dictate how the others fit alongside it.

Here's how the Agency is modeled in Tangible Cred (TCred):

The Agency and all of its properties
class Agency extends Aggregate {
# Scalar values
private string $organisation_code;

private string $display_name;

private string $website;

/**
* @var string Organization help text from Figma
*/
private string $description;

private TimedFrontendNotice $frontend_notice;

/**
* @var array<int, string>
*/
private array $profession_codes;

/**
* @var int[]
*/
private array $point_types;

/**
* @var int[]
*/
private array $regions;

/**
* @var array<int, PointExpiration>
*/
private array $point_expirations;

private int $default_certificate_id;

private AgencyEnrolmentType $enrolment_type;

/**
* @var IFieldSpecification[]
*/
private array $agency_accreditation_fields;


/**
* @var IFieldSpecification[]
*/
private array $agency_license_fields;

/**
* @var IFieldSpecification[]
*/
private array $agency_user_fields;

public function __construct(
?int $id = null,
string $organisation_code = '',
string $display_name = '',
string $website = '',
string $description = '',

?TimedFrontendNotice $frontend_notice = null,
array $point_types = [],
array $regions = [],
array $profession_codes = [],

array $point_expirations = [],
int $default_certificate_id = 0,
AgencyEnrolmentType $enrolment_type = AgencyEnrolmentType::manual,

array $agency_accreditation_fields = [],
array $agency_license_fields = [],
array $agency_user_fields = []
) {
tgbl_cred_assert_type( $point_types, 'int', 'Point types must be integers');
tgbl_cred_assert_type( $point_expirations, PointExpiration::class, 'Each point expiration must be an instance of PointExpiration' );
tgbl_cred_assert_type( $agency_accreditation_fields, IFieldSpecification::class, 'Each agency accreditation field must be an instance of IFieldSpecification' );
tgbl_cred_assert_type( $agency_license_fields, LicenseFieldSpecification::class, 'Each agency license field must be an instance of IFieldSpecification' );
tgbl_cred_assert_type( $agency_user_fields, IFieldSpecification::class, 'Each agency user field must be an instance of IFieldSpecification' );

I've added a few lines from the constructor to get a feeling of how we're enforcing the type rules. There are getters for all of these values; setters only on an as-needed basis.

An astute observer will notice later on that some of this code is already outdated - one will notice (in later sections, as of yet not written) that we've begun using specialised ___List objects (IntList, StringList, FieldSpecificationList, etc.), but here we're using generic arrays whose type adherence we check using tgbl_cred_assert_type. The reader should feel encouraged by this - we don't need optimal soutions, we need good solutions whose trade-offs we understand. In this case, using arrays is easy, but we can forget the typecheck. Using a typed list is easier, but we need to create another class. No generics for us yet !

Let us then think about what we're looking at !

We call this large object in the software philosophy that inspired and is in active use in Tangible Cred (Domain-Driven Design) an Aggregate.

An Aggregate is a fancy name for a type of Entity (another fancy name here - an Entity is essentially anything with a lifecycle/identity based on a unique ID such as an autoincremented integer). An "Aggregate" is responsible for enforcing its own rules regarding internal consistency.